/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using CodeGenerator.Definitions;
using System.Text;
using System.Reflection;
using EXtremecode.Logging;
using EXtremecode.Utils;
using System.IO;
using System.Xml;
using EXtremecode.Common;


namespace CodeGenerator.Managers
{
	
	public delegate bool FileAlreadyExsistsDelegate(string fileName);
	//return value will decide whether allow to overwrite or not.

	public delegate void RepositoryErrorDelegate(string errorMessage);

	/// <summary>
	/// Summary description for RepositoryManager.
	/// </summary>
	public class RepositoryManager
	{
		private DirectoryInfo dirInfoRepository,dirInfoOutFiles;
		private static RepositoryManager singleton=null;
		private Repository repository;
		private SessionCollection sessionCol;
		private Entity activeEntity; // will be used by code template. 
		private EntityCollection entities;

		public static event DoYouWantToDelegate DoYouWantToOverwriteFile;
		public RepositoryErrorDelegate	RepositoryError;
		public static event DoYouWantToDelegate DoYouWantToGenerateEntity;
		public SessionCollection SessionCol
		{
			get
			{
				return sessionCol;
			}
		}
		public Repository  Repository
		{
			get
			{
				return repository;
			}
		}
		public Entity ActiveEntity
		{
			get
			{
				return activeEntity;
			}
		}

		public EntityCollection Entities
		{
			get
			{
				return entities;
			}
		}
		private RepositoryManager(string repositoryDirPath,string outputDirPath)
		{
			dirInfoOutFiles=new DirectoryInfo(outputDirPath);
			dirInfoRepository =new DirectoryInfo(repositoryDirPath);

			//load repository
			XmlDocument xmlDocRepository=  new XmlDocument();
			xmlDocRepository.Load(string.Format("{0}\\Repository.xml",dirInfoRepository.FullName));
			repository=new CodeGenerator.Definitions.Repository(xmlDocRepository);

			//load sessions
			sessionCol=new SessionCollection();
			foreach(Repository.SessionFiles.File file in repository.SessionFilesList)
			{
				FileInfo[] fileInfos =dirInfoRepository.GetFiles(file.Name);
				
				if(fileInfos.Length>0)
				{
					sessionCol.LoadSessions(fileInfos[0].FullName);
				}
			}

			//initialize GeneratingCodeManager
			GeneratingCodeManager.CreateNewInstance(sessionCol);

			//Load Entities
			XmlDocument xmlDocEntityDefinitoin=  new XmlDocument();
			XmlDocument xmlDocEntityCustom=  new XmlDocument();

			xmlDocEntityDefinitoin.Load(string.Format("{0}\\{1}",dirInfoRepository.FullName,repository.EntityDefinitionFile));
			xmlDocEntityCustom.Load(string.Format("{0}\\{1}",dirInfoRepository.FullName,repository.EntityCustomFile));
			entities=new CodeGenerator.Definitions.EntityCollection(xmlDocEntityDefinitoin,xmlDocEntityCustom);

		}
        private void GenerateCodeFile(Repository.GeneratedFiles.File generatedFileInfo,
            FileInfo fileInfoGenerated)
        {


            //if file is disabled then just do return
            if (!generatedFileInfo.Enabled)
            {
                return;
            }

            if (generatedFileInfo.sessionList.Length > 0)
            {
                StringBuilder newCode =
                    GeneratingCodeManager.Instance.GetSessionsResult
                    (generatedFileInfo.sessionList);

                bool overwrite = true;
                if (fileInfoGenerated.Exists)
                {
                    if (DoYouWantToOverwriteFile != null)
                    {
                        overwrite = DoYouWantToOverwriteFile(
                            string.Format("Do you want to overwrite this file \n{0}",
                            fileInfoGenerated.FullName));
                    }

                    //custom code mapping.
                    if (overwrite && generatedFileInfo.MappCustomCode)
                    {
                        StringBuilder oldCode =
                            FileUtil.ReadFileData(fileInfoGenerated.FullName);
                        CustomCodeExchangeManager.Instance.ExchangeCustomCode(newCode, oldCode);
                    }
                }
                else
                {
                    fileInfoGenerated.Directory.Create();
                }

                if (overwrite)
                {

                    FileStream stream = fileInfoGenerated.Open(FileMode.Create,
                        FileAccess.ReadWrite, FileShare.Read);
                    StreamWriter writer = new StreamWriter(stream);
                    writer.Write(newCode.ToString());
                    writer.Flush();
                    writer.Close();
                }

            }
            else
            {
                if (RepositoryError != null)
                {
                    RepositoryError(
                        string.Format("Generated File Name [{0}]:No Session Defined",
                        generatedFileInfo.Name));
                }
            }

        }
		public void GenerateCodeFiles()
		{
            try
            {
                bool firstRepositoryItration = true;
                //will be used in generating files once, having type 'once' or 'copy'

                foreach (string key in entities.Keys)
                {
                    Entity entity = entities[key];
                    activeEntity = entity;
                    string entityFileNamePrefix = StringProcessor.FileName(entity.Name);
                    string entityFolderName = StringProcessor.Namespace(entity.AccessorName);
                    foreach (Repository.GeneratedFiles.File generatedFileInfo in repository.GeneratedFilesList)
                    {
                        //Type:CopyFolder
                        DirectoryInfo directoryInfoGenerated = null;
                        if (generatedFileInfo.Type == GeneratedFileType.CopyFolder)
                        {
                            if (firstRepositoryItration && generatedFileInfo.Enabled)
                            {
                                directoryInfoGenerated = new DirectoryInfo(dirInfoOutFiles.FullName);
                                if (generatedFileInfo.CopyFile != null && generatedFileInfo.CopyFile != String.Empty)
                                {
                                    DirectoryInfo sourceDir = new DirectoryInfo(dirInfoRepository + "\\" + generatedFileInfo.CopyFile);
                                    if (sourceDir.Exists)
                                    {
                                        bool overwrite = true;
                                        if (directoryInfoGenerated.Exists)
                                        {
                                            if (DoYouWantToOverwriteFile != null)
                                            {
                                                overwrite = DoYouWantToOverwriteFile(string.Format("This folder already exists.\n" +
                                                    "Do you want to overwrite this folder and all contained files. \n{0}",
                                                    directoryInfoGenerated.FullName));
                                            }
                                        }
                                        if (overwrite)
                                        {
                                            FileUtil.CopyFiles(sourceDir, directoryInfoGenerated);
                                        }


                                    }
                                    else
                                    {
                                        if (RepositoryError != null)
                                        {
                                            RepositoryError(
                                                string.Format("Source Directory [{0}]:File '{1}'doesnt exist",
                                                generatedFileInfo.Name, sourceDir.FullName));
                                        }
                                    }
                                }
                                else
                                {
                                    if (RepositoryError != null)
                                    {
                                        RepositoryError(
                                            string.Format("Source Directory [{0}] not defined",
                                            generatedFileInfo.Name));
                                    }
                                }

                            }
                        }


                        //Type:Copy
                        FileInfo fileInfoGenerated = null;
                        if (generatedFileInfo.Type == GeneratedFileType.Copy)
                        {
                            if (firstRepositoryItration)
                            {
                                fileInfoGenerated = FileUtil.GetFileInfo(generatedFileInfo.Name, dirInfoOutFiles);
                                if (generatedFileInfo.CopyFile != null && generatedFileInfo.CopyFile != String.Empty)
                                {
                                    FileInfo copyFile =
                                        FileUtil.GetFileInfo(generatedFileInfo.CopyFile, dirInfoRepository);
                                    if (copyFile.Exists)
                                    {
                                        bool overwrite = true;
                                        if (fileInfoGenerated.Exists)
                                        {
                                            if (DoYouWantToOverwriteFile != null)
                                            {
                                                overwrite = DoYouWantToOverwriteFile(string.Format("Do you want to overwrite this file \n{0}",
                                                    fileInfoGenerated.FullName));
                                            }
                                        }
                                        if (overwrite)
                                        {
                                            copyFile.CopyTo(fileInfoGenerated.FullName, true);
                                        }

                                    }
                                    else
                                    {
                                        if (RepositoryError != null)
                                        {
                                            RepositoryError(
                                                string.Format("Generated File Name [{0}]:File '{1}'doesnt exist",
                                                generatedFileInfo.Name, copyFile));
                                        }
                                    }
                                }
                                else
                                {
                                    if (RepositoryError != null)
                                    {
                                        RepositoryError(
                                            string.Format("Generated File Name [{0}]:Copy File not defined",
                                            generatedFileInfo.Name));
                                    }
                                }

                            }
                        }


                            //Type:Once
                        else if (generatedFileInfo.Type == GeneratedFileType.Once)
                        {
                            if (firstRepositoryItration)
                            {
                                fileInfoGenerated = FileUtil.GetFileInfo(generatedFileInfo.Name, dirInfoOutFiles);
                                GenerateCodeFile(generatedFileInfo, fileInfoGenerated);
                            }
                        }

                        //Type:Table
                        else if (generatedFileInfo.Type == GeneratedFileType.Table)
                        {
                            bool wantToGenerate = true;
                            if (DoYouWantToGenerateEntity != null)
                            {
                                wantToGenerate = DoYouWantToGenerateEntity(entity.Name);
                            }
                            if (wantToGenerate)
                            {
                                fileInfoGenerated = FileUtil.GetFileInfo(string.Format(generatedFileInfo.Name, entityFileNamePrefix),
                                    dirInfoOutFiles);
                                fileInfoGenerated = FileUtil.ConvertFileInfoInSubfolder(fileInfoGenerated, entityFolderName);
                                GenerateCodeFile(generatedFileInfo, fileInfoGenerated);
                            }
                        }
                    }
                    firstRepositoryItration = false;
                }
            }
            finally
            {
                
            }

		}

		/// <summary>
		/// Instance
		/// </summary>
		public static RepositoryManager Instance
		{
			get
			{
				if(singleton==null)
				{
					throw Logger.Write(new Exception("Singleton object not created till yet"));
				}
				return singleton;
			}
		}
		
		/// <summary>
		/// CreateInstance
		/// </summary>
		/// <param name="sessions"></param>
		/// <returns></returns>
		public static RepositoryManager CreateInstance(string repositoryDirPath,string outputDirPath)
		{
			
			if(singleton==null)
			{
				singleton=new RepositoryManager(repositoryDirPath,outputDirPath);
			}
			return singleton;
		}

		public static RepositoryManager CreateNewInstance(string repositoryDirPath,string outputDirPath)
		{
			//we have to avoid signleton here because user may modify template and want to regnerate
			//code with new changes.
			//generating code using this instace so it must be stored on singleton variable.
			singleton =  new RepositoryManager(repositoryDirPath,outputDirPath);
			return singleton; 
		}
		
	}
}
